/*************************************************************************
        > File Name: opitem.cpp
        > Author: xuwenlong
        > Mail: myxuan475@126.com
        > Created Time: 2018年02月23日 星期五 15时01分24秒
 ************************************************************************/

#include "opitem.h"
#include <queue>
#include <stack>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <utils/custom.h>
#include <utils/logger.h>
#include <vector>

namespace libxcom {
OpItem::OpItem(const char ch)
{
    type = OP_NONE;
    params = 0;
    switch (ch) {
        case '(':
        case ')':
            type = OP_PARENTHESS;
            break;
        case '[':
        case ']':
            type = OP_BRACKET;
            break;
        case '{':
        case '}':
            type = OP_BRACE;
            break;
        case '+':
        case '-':
        case '*':
        case '/':
        case '%':
        case '&':
        case '^':
            type = OP_BASE;
            break;
        default:
            break;
    }

    op.ch = ch;
}

OpItem::OpItem(const std::string &func, const char prefix)
{
#define STREQUAL(str) (func == str)

    type = OP_NONE;
    params = 0;
    if (prefix == 0) {
        bool isfloat = false;
        if (utils::isDecimal(func.c_str(), isfloat)) {
            type = OP_NUM;
            if (isfloat) {
                digit = atof(func.c_str());
            } else {
                digit = atoi(func.c_str());
            }
        } else if (utils::isHexDigit(func.c_str())) {
            type = OP_NUM;
            uint32_t v = utils::strtohex(func.c_str());
            if (v <= 255)
                digit = (uint8_t)v;
            else
                digit = v;
        }
    } else {
        if (STREQUAL("pos"))
            type = OP_VAR;
        else if (STREQUAL("chk") || STREQUAL("ref"))
            type = OP_FUNC;
        else if (STREQUAL("ditch") || STREQUAL("fill"))
            type = OP_KEYFUNC;
        else if (utils::isInteger(func.c_str()))
            type = OP_VAR;
        else if (prefix == '&')
            type = OP_VAR;
        else
            type = OP_FUNC;
        memset(op.var, 0, MAX_VAR_LEN);
        op.var[0] = prefix;
        strncpy(&op.var[1], func.c_str(), MAX_VAR_LEN - 1);
    }

#undef STREQUAL
}

OpItem::OpItem(const OpItem &item)
{
    type = OP_NONE;
    copyitem(item);
}

OpItem::OpItem()
{
    params = 0;
    type = OP_NONE;
}

OpItem::~OpItem()
{
    //    if (type == OP_VAR ||type == OP_FUNC ||type == OP_KEYFUNC)
    //        free(op.var);
    release();
}

OpItem &OpItem::operator=(const OpItem &item)
{
    release();
    copyitem(item);
    return *this;
}

bool OpItem::isDigtal(const char *var)
{
    bool isfloat = false;
    if (utils::isDecimal(var, isfloat)) {
        return true;
    } else if (utils::isHexDigit(var)) {
        return true;
    }
    return false;
}

bool OpItem::isKeyWords(const std::string &func)
{
#define STREQUAL(var) (func == var)
    if (STREQUAL("pos"))
        return true;
    else if (STREQUAL("chk") || STREQUAL("ref"))
        return true;
    else if (STREQUAL("ditch") || STREQUAL("fill"))
        return true;
    else
        return false;
}

bool OpItem::isFormatChar(const char c)
{
    return (c == '{' || c == '}' || c == '[' || c == ']' || c == '(' || c == ')' || c == '+' || c == '-' || c == '*' ||
        c == '/' || c == '%' || c == '^' || c == '|' || c == '!' || c == '&' || c == ' ' || c == ',');
}

bool OpItem::isSupport(const char c, int np)
{
    if (c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']')
        return true;
    if (np == 1) {
        if (c == '!' || c == '&' || c == '-')
            return true;
    } else if (np == 2) {
        if (c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^' || c == '|' || c == '&')
            return true;
    }
    return false;
}

void OpItem::copyitem(const OpItem &item)
{
    params = item.params;
    type = item.type;
    if (type == OP_VAR || type == OP_FUNC || type == OP_KEYFUNC) {
        memset(op.var, 0, MAX_VAR_LEN);
        strncpy(op.var, item.op.var, MAX_VAR_LEN);
    } else {
        if (type == OP_NUM) {
            digit = item.digit;
        } else
            op.ch = item.op.ch;
    }
}

/*外 优先级 ,数字优先级设为-1 */
int OpItem::priority()
{
    switch (type) {
        case OP_BASE: // 基本运算符，+-×/等
            if (op.ch == '+' || op.ch == '-')
                return 4;
            else if (op.ch == '*' || op.ch == '/' || op.ch == '%')
                return 6;
            else if (op.ch == '&' || op.ch == '^')
                return 7;
        default:
            return 10;
    }
}

std::string OpItem::showStr()
{
    char buf[64] = {0};
    if (type == OP_NUM) {
        if (digit.IsInteger()) {
            if (digit.type == TYPE_U8 || digit.type == TYPE_CMD)
                sprintf(buf, "%02XH", (uint8_t)digit.Integer());
            else
                sprintf(buf, "%" PRId64, digit.Integer());
        } else {
            sprintf(buf, "%0.3f", digit.Float());
        }
    } else if (type == OP_BASE || type == OP_BRACKET || type == OP_BRACE || type == OP_PARENTHESS) {
        if (op.ch == '(' || op.ch == ')')
            strcat(buf, "()");
        else if (op.ch == '[' || op.ch == ']')
            strcat(buf, "[]");
        else if (op.ch == '{' || op.ch == '}')
            strcat(buf, "{}");
        else
            buf[0] = op.ch;
    } else if (type == OP_VAR || type == OP_FUNC || type == OP_KEYFUNC) {
        sprintf(buf, "%s", op.var);
    } else
        strcat(buf, "null");
    return buf;
}

std::string OpItem::leftStr()
{
    if (type == OP_BASE || type == OP_BRACKET || type == OP_BRACE || type == OP_PARENTHESS) {
        if (op.ch == '(' || op.ch == ')')
            return "(";
        else if (op.ch == '[' || op.ch == ']')
            return "[";
        else if (op.ch == '{' || op.ch == '}')
            return "{";
        else
            return "";
    } else
        return showStr();
}

std::string OpItem::rightStr()
{
    if (type == OP_BASE || type == OP_BRACKET || type == OP_BRACE || type == OP_PARENTHESS) {
        if (op.ch == '(' || op.ch == ')')
            return ")";
        else if (op.ch == '[' || op.ch == ']')
            return "]";
        else if (op.ch == '{' || op.ch == '}')
            return "}";
        else
            return "";
    } else
        return showStr();
}

void OpItem::release()
{
    //    if (type == OP_NUM && (op.digit != NULL)) {
    //        delete op.digit;
    //        op.digit = NULL;
    //    }
    type = OP_NONE;
}
} // namespace libxcom
